package com.yumeng.framework.persistence.mybatis.base.dao;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.LambdaUtils;
import com.baomidou.mybatisplus.core.toolkit.ReflectionKit;
import com.baomidou.mybatisplus.core.toolkit.support.ColumnCache;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.QueryChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.UpdateChainWrapper;
import com.baomidou.mybatisplus.extension.toolkit.ChainWrappers;
import com.yumeng.common.api.PageData;
import com.yumeng.common.auth.BaseAuthHelper;
import com.yumeng.common.data.enums.ParamOptType;
import com.yumeng.common.exception.NormalDBException;
import com.yumeng.common.helper.AssertHelper;
import com.yumeng.common.helper.DbHelper;
import com.yumeng.config.helper.PropHelper;
import com.yumeng.config.properties.YumengMybatisPlusProperties;
import com.yumeng.framework.persistence.mybatis.base.entity.MpBaseEntity_CD;
import com.yumeng.framework.persistence.mybatis.base.entity.MpBaseEntity_CUD;
import com.yumeng.framework.persistence.mybatis.base.page.MyPage;
import com.yumeng.framework.persistence.mybatis.base.page.param.ComplexParamItem;
import com.yumeng.framework.persistence.mybatis.data.MybatisPlusExtConstants;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.executor.BatchResult;
import org.springframework.beans.factory.annotation.Autowired;

import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 基于Mybatis-plus 公共Dao
 * 装饰器模式 整合Mapper和Dao
 *
 * implements MyBaseMapper<T> 废弃
 * 不继承MyBaseMapper，仅根据需要重构MyBaseMapper中的方法
 *
 * @author wxd
 * @date 2025/10/10 15:53
 */
public abstract class MpBaseDao<M extends MyBaseMapper<T>, T> {

    private static YumengMybatisPlusProperties mybatisPlusProperties;
    private static YumengMybatisPlusProperties getMybatisPlusProperties() {
        if (mybatisPlusProperties == null) {
            mybatisPlusProperties = PropHelper.getMybatisPlusProperties();
        }
        return mybatisPlusProperties;
    }

    private M baseMapper;
    @Autowired
    public void setBaseMapper(M baseMapper) {
        this.baseMapper = baseMapper;
    }
    public M mapper() {
        return baseMapper;
    }

    private Class<T> entityClass;
    private Class<M> mapperClass;
    private TableInfo tableInfo;
    private Boolean hasLogicDelete;

    public Class<T> getEntityClass() {
        if (entityClass == null){
            entityClass = currentModelClass();
        }
        return entityClass;
    }
    protected Class<M> getMapperClass() {
        if (mapperClass == null){
            mapperClass  = currentMapperClass();
        }
        return mapperClass;
    }
    /**
     * 是否包含逻辑删除
     * @return
     */
    public Boolean getHasLogicDelete() {
        if (hasLogicDelete == null){
            hasLogicDelete = hasLogicDelete();
        }
        return hasLogicDelete;
    }
    /**
     * 获取表名
     * @return
     */
    protected String getTableName() {
        TableInfo tableInfo = getTableInfo();
        return tableInfo.getTableName();
    }
    /**
     * 获取主键列名
     * @return
     */
    public String getKeyColumn(){
        TableInfo tableInfo = getTableInfo();
        return tableInfo.getKeyColumn();
    }

    private Class<M> currentMapperClass() {
        return (Class<M>) ReflectionKit.getSuperClassGenericType(this.getClass(), MpBaseDao.class, 0);
    }
    private Class<T> currentModelClass() {
        return (Class<T>) ReflectionKit.getSuperClassGenericType(this.getClass(), MpBaseDao.class, 1);
    }
    private boolean hasLogicDelete(){
        TableInfo tableInfo = getTableInfo();
        for (TableFieldInfo fieldInfo : tableInfo.getFieldList()){
            if (fieldInfo.getColumn().equals(getMybatisPlusProperties().getLogicDeleteField())){
                return true;
            }
        }
        return false;
    }
    private TableInfo getTableInfo(){
        if (tableInfo == null){
            tableInfo = TableInfoHelper.getTableInfo(getEntityClass());
        }
        return tableInfo;
    }

    //region Wrapper 参考 com.baomidou.mybatisplus.extension.repository.IRepository

    /**
     * 链式查询 普通
     *
     * @return QueryWrapper 的包装类
     */
    protected QueryChainWrapper<T> query() {
        return ChainWrappers.queryChain(mapper());
    }

    /**
     * 链式查询 lambda 式
     * <p>注意：不支持 Kotlin </p>
     *
     * @return LambdaQueryWrapper 的包装类
     */
    protected LambdaQueryChainWrapper<T> lambdaQuery() {
        return ChainWrappers.lambdaQueryChain(mapper(), getEntityClass());
    }

    /**
     * 链式更改 普通
     *
     * @return UpdateWrapper 的包装类
     */
    protected UpdateChainWrapper<T> update() {
        return ChainWrappers.updateChain(mapper());
    }

    /**
     * 链式更改 lambda 式
     * <p>注意：不支持 Kotlin </p>
     *
     * @return LambdaUpdateWrapper 的包装类
     */
    protected LambdaUpdateChainWrapper<T> lambdaUpdate() {
        return ChainWrappers.lambdaUpdateChain(mapper());
    }

    //endregion

    //region 基础操作

    //===========================================================================================新增
    public boolean insertRetBool(T entity) {
        return DbHelper.retBool(mapper().insert(entity), "插入失败");
    }

    public boolean insertBatchRetBool(Collection<T> entityList){
        List<BatchResult> results = mapper().insert(entityList);
        int count = getBatchResultTotalCount(results);
        return DbHelper.retBool(count, entityList.size(), "批量插入失败");
    }

    //===========================================================================================删除
    public boolean deleteByIdRetBool(Serializable id) {
        //由于不启用框架的逻辑删除，因此，传false
        return DbHelper.retBool(mapper().deleteById(id, false), "删除失败");
    }
    public boolean deleteByIdRetBool(T entity) {
        return DbHelper.retBool(mapper().deleteById(entity), "删除失败");
    }
    public boolean deleteByMapRetBool(Map<String, Object> columnMap) {
        return DbHelper.retBoolWeak(mapper().deleteByMap(columnMap), "条件删除失败");
    }
    public boolean deleteRetBool(Wrapper<T> queryWrapper) {
        return DbHelper.retBoolWeak(mapper().delete(queryWrapper), "条件删除失败");
    }
    public boolean deleteByIdsRetBool(Collection<?> idList) {
        //由于不启用框架的逻辑删除，因此，传false
        return DbHelper.retBoolWeak(mapper().deleteByIds(idList, false), "批量删除失败");
    }

    //===========================================================================================更新
    public boolean updateByIdRetBool(T entity) {
        //如果属性未变，结果可能为0，但也不需要抛出异常来，因此默认不抛出异常
        return DbHelper.retBool(mapper().updateById(entity), "更新失败", false);
    }
    public boolean updateRetBool(T entity, Wrapper<T> updateWrapper) {
        return DbHelper.retBoolWeak(mapper().update(entity, updateWrapper), "条件更新失败");
    }
    public boolean updateRetBool(Wrapper<T> updateWrapper) {
        return DbHelper.retBoolWeak(mapper().update(updateWrapper), "条件更新失败");
    }

    public boolean updateByIdsRetBool(Collection<T> entityList){
        List<BatchResult> results = mapper().updateById(entityList);
        int count = getBatchResultTotalCount(results);
        return DbHelper.retBoolWeak(count, "批量更新失败");
    }

    //===========================================================================================查询

    public T selectByIdForLogicDelete(Serializable id){
        if (!getHasLogicDelete()){
            throw new NormalDBException("当前数据不支持逻辑删除模式查询");
        }
        T t = mapper().selectById(id);
        if(t != null){
            Integer unLogicDeleteVal = Integer.parseInt(getMybatisPlusProperties().getUnLogicDeleteVal());
            if (t instanceof MpBaseEntity_CUD entity){
                if (!unLogicDeleteVal.equals(entity.getDelFlag())){
                    throw new NormalDBException("数据已被删除");
                }
            }if (t instanceof MpBaseEntity_CD entity){
                if (!unLogicDeleteVal.equals(entity.getDelFlag())){
                    throw new NormalDBException("数据已被删除");
                }
            }else{
                throw new NormalDBException("当前数据不支持逻辑删除模式查询");
            }
        }
        return t;
    }

    public List<T> selectByIdsWithCheck(Collection<? extends Serializable> idList, boolean checkCount){
        List<T> list = mapper().selectByIds(idList);
        DbHelper.checkDataCount(list.size(), idList.size(), checkCount);
        return list;
    }

    /**
     * 根据 Wrapper 条件，查询全部记录
     *
     * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
     * @param mapper       转换函数
     */
    public  <V> List<V> listObjs(Wrapper<T> queryWrapper, Function<? super Object, V> mapper) {
        return mapper().selectObjs(queryWrapper).stream().filter(Objects::nonNull).map(mapper).collect(Collectors.toList());
    }

    //===========================================================================================分页
    public Long selectPageCount(MyPage<T> page){
        QueryWrapper<T> queryWrapper = createQueryWrapper(page);
        return mapper().selectCount(queryWrapper);
    }

    public IPage<T> selectPage(MyPage<T> page) {
        QueryWrapper<T> queryWrapper = createQueryWrapper(page);
        return mapper().selectPage(page, queryWrapper);
    }

    //流式处理
/*    public IPage<T> selectPage(MyPage<T> page, int a) {
        QueryWrapper<T> queryWrapper = createQueryWrapper(page);
        mapper().selectList(page, queryWrapper, new ResultHandler<>() {
            int count = 0;
            @Override
            public void handleResult(ResultContext<? extends T> resultContext) {
                T obj = resultContext.getResultObject();
                //System.out.println("当前处理第" + (++count) + "条记录: " + h2User);
                // 在这里进行你的业务处理，比如分发任务
            }
        });
        return page;
    }*/

    private QueryWrapper<T> createQueryWrapper(MyPage<T> page){
        QueryWrapper<T> queryWrapper = null;
        if (MapUtils.isNotEmpty(page.getComplexParam())){
            queryWrapper = new QueryWrapper<>();
            Map<String, ColumnCache> columnMap = getColumnMap();
            for (Map.Entry<String, ComplexParamItem> entry : page.getComplexParam().entrySet()){
                ComplexParamItem paramItem = entry.getValue();
                Object val = paramItem.getVal();
                ParamOptType optType = paramItem.getOptType();
                String colName = paramItem.getColName();
                if (StringUtils.isBlank(colName)){
                    String propName = entry.getKey();
                    ColumnCache columnCache = getColumnCache(propName, columnMap);
                    if (columnCache != null){
                        colName = columnCache.getColumn();
                    }
                    if (StringUtils.isBlank(colName)){
                        throw new NormalDBException("参数【%s】对应的字段未知");
                    }
                }
                if (ParamOptType.EQ.equals(optType)){
                    queryWrapper.eq(colName, val);
                }else if (ParamOptType.NE.equals(optType)){
                    queryWrapper.ne(colName, val);
                }else if (ParamOptType.GT.equals(optType)){
                    queryWrapper.gt(colName, val);
                }else if (ParamOptType.GE.equals(optType)){
                    queryWrapper.ge(colName, val);
                }else if (ParamOptType.LT.equals(optType)){
                    queryWrapper.lt(colName, val);
                }else if (ParamOptType.LE.equals(optType)){
                    queryWrapper.le(colName, val);
                }else if (ParamOptType.LIKE.equals(optType)){
                    queryWrapper.like(colName, val);
                }else if (ParamOptType.NOT_LIKE.equals(optType)){
                    queryWrapper.notLike(colName, val);
                }else if (ParamOptType.LIKE_LEFT.equals(optType)){
                    queryWrapper.likeLeft(colName, val);
                }else if (ParamOptType.LIKE_RIGHT.equals(optType)){
                    queryWrapper.likeRight(colName, val);
                }else{
                    throw new NormalDBException("暂不支持该参数操作类型：%s", optType);
                }
            }
        }
        return queryWrapper;
    }

    //endregion

    //region 扩展操作
    //=============================以下内置的选装件
/*    public boolean alwaysUpdateSomeColumnByIdRetBool(T entity) {
        return DbHelper.retBool(mapper().alwaysUpdateSomeColumnById(entity), "更新失败", false);
    }*/

    //=============================以下为自己自定义

    public boolean logicDeleteByIdRetBool(Serializable id){
        return DbHelper.retBool(logicDeleteById(id), "删除失败");
    }
    public int logicDeleteById(Serializable id){
        AssertHelper.assertNotNull(id, "logicDeleteById 缺少参数id");
        Map<String, Object> columnMap = new HashMap<>();
        columnMap.put(getMybatisPlusProperties().getCommKeyProp(), id);
        setLogicDeleteExtInfo(columnMap);
        return mapper().logicDeleteById(columnMap);
    }

    public boolean logicDeleteByIdRetBool(T entity){
        return DbHelper.retBool(logicDeleteById(entity), "删除失败");
    }
    public int logicDeleteById(T t) {
        if (t instanceof MpBaseEntity_CUD entity){
            setLogicDeleteExtInfo(entity);
        }if (t instanceof MpBaseEntity_CD entity){
            setLogicDeleteExtInfo(entity);
        }else{
            throw new NormalDBException("当前数据不支持逻辑删除");
        }
        return mapper().updateById(t);
    }


    public boolean logicDeleteByIdsRetBool(List<? extends Serializable> idList) {
        return DbHelper.retBool(logicDeleteByIds(idList), idList.size(), "批量删除失败");
    }
    public boolean logicDeleteByIdsRetBool(List<? extends Serializable> idList, boolean weak) {
        int result = logicDeleteByIds(idList);
        if (weak){
            return DbHelper.retBoolWeak(result, "批量删除失败");
        }
        return DbHelper.retBool(result, idList.size(), "批量删除失败");
    }
    public int logicDeleteByIds(List<? extends Serializable> idList) {
        AssertHelper.assertNotEmpty(idList, "logicDeleteByIds 参数idList集合为空");
        Map<String, Object> columnMap = new HashMap<>();
        columnMap.put(getMybatisPlusProperties().getCommKeyProps(), idList);
        setLogicDeleteExtInfo(columnMap);
        return mapper().logicDeleteByIds(columnMap);
    }

    public int logicDelete(Wrapper<T> wrapper) {
        setLogicDeleteExtInfo((UpdateWrapper<T>) wrapper);
        return mapper().logicDelete(wrapper);
    }
    public boolean logicDeleteRetBool(UpdateWrapper<T> wrapper) {
        return DbHelper.retBoolWeak(logicDelete(wrapper), "条件删除失败");
    }

    public int logicDeleteByMap(Map<String, Object> columnMap) {
        Map<String, Object> columnMapExt = new HashMap<>();
        setLogicDeleteExtInfo(columnMapExt);
        columnMap.put(MybatisPlusExtConstants.LOGIC_DEL_EXT, columnMapExt);
        return mapper().logicDeleteByMap(columnMap);
    }
    public boolean logicDeleteByMapRetBool(Map<String, Object> columnMap) {
        return DbHelper.retBoolWeak(logicDeleteByMap(columnMap), "条件删除失败");
    }
    //endregion

    //region 辅助

    protected PageData<T> buildPage(IPage<T> page){
        return new PageData<>(page.getTotal(), page.getRecords(), page.getCurrent(), page.getSize());
    }
    //单独指定泛型类型
    protected <P> PageData<P> buildObjPage(IPage<P> page){
        return new PageData<>(page.getTotal(), page.getRecords(), page.getCurrent(), page.getSize());
    }

    //endregion

    //region 私有

    /**
     * 设置逻辑删除扩展信息
     */
    private void setLogicDeleteExtInfo(Map<String, Object> columnMap){
        columnMap.put(getMybatisPlusProperties().getLogicDeleterProp(), BaseAuthHelper.getCurrAuthInfo().getIdentityId());
        columnMap.put(getMybatisPlusProperties().getLogicDeleteTimeProp(), LocalDateTime.now());
    }
    private void setLogicDeleteExtInfo(UpdateWrapper<T> wrapper){
        wrapper.set(getMybatisPlusProperties().getLogicDeleterField(), BaseAuthHelper.getCurrAuthInfo().getIdentityId());
        wrapper.set(getMybatisPlusProperties().getLogicDeleteTimeField(), LocalDateTime.now());
    }
    private void setLogicDeleteExtInfo(MpBaseEntity_CUD baseEntity){
        baseEntity.setDeler(BaseAuthHelper.getCurrAuthInfo().getIdentityIdLongVal());
        baseEntity.setDelTime(LocalDateTime.now());
        baseEntity.setDelFlag(Integer.parseInt(getMybatisPlusProperties().getLogicDeleteVal()));
    }
    private void setLogicDeleteExtInfo(MpBaseEntity_CD baseEntity){
        baseEntity.setDeler(BaseAuthHelper.getCurrAuthInfo().getIdentityIdLongVal());
        baseEntity.setDelTime(LocalDateTime.now());
        baseEntity.setDelFlag(Integer.parseInt(getMybatisPlusProperties().getLogicDeleteVal()));
    }

    private ColumnCache getColumnCache(String fieldName) {
        Map<String, ColumnCache> columnMap = getColumnMap();
        ColumnCache columnCache = getColumnCache(fieldName, columnMap);
        return columnCache;
    }
    private Map<String, ColumnCache> getColumnMap(){
        return LambdaUtils.getColumnMap(getEntityClass());
    }
    private ColumnCache getColumnCache(String fieldName, Map<String, ColumnCache> columnMap){
        ColumnCache columnCache = columnMap.get(LambdaUtils.formatKey(fieldName));
        AssertHelper.assertNotNull(columnCache, String.format("未找到columnCache，实体类【%s】，属性【%s】", getEntityClass().getName(), fieldName));
        return columnCache;
    }

    private int getBatchResultTotalCount(List<BatchResult> results){
        int totalCount = 0;
        for (BatchResult batchResult : results) {
            totalCount += Arrays.stream(batchResult.getUpdateCounts()).sum();
        }
        return totalCount;
    }

    //endregion
}
